/**
 * 将Base64编码转换为Blob
 * @param base64 传入Base64格式编码
 */
export function base64Img2Blob(base64: string): Blob {
  const parts = base64.split(';base64,');
  if (parts.length < 2) { throw Error('base64 was Error'); }
  const contentType = parts[0].split(':')[1];
  const raw = window.atob(parts[1]);
  const rawLength = raw.length;

  const uInt8Array = new Uint8Array(rawLength);
  for (let i = 0; i < rawLength; i += 1) {
    uInt8Array[i] = raw.charCodeAt(i); // 返回UTF-16码点
  }
  return new Blob([uInt8Array], { type: contentType });
}

/**
 * 根据图片的Base64编码进行模拟a标签下载
 * @param fileName 导出后的图片文件名称
 * @param content 图片的Base64数据
 */
export function downloadImage(fileName: string, content: string): boolean {
  const aLink = document.createElement('a');
  const blob = base64Img2Blob(content); // new Blob([content]);
  if (!blob) { return false; }
  const evt = document.createEvent('HTMLEvents');
  evt.initEvent('click', false, false); // initEvent 不加后两个参数在FF下会报错
  aLink.download = fileName;
  aLink.href = URL.createObjectURL(blob);
  aLink.click();
  aLink.dispatchEvent(evt);
  return true;
}

export function loadImage(imageSrc: string): Promise<HTMLImageElement> {
  const image = new Image();
  image.src = imageSrc;
  return new Promise((resolve, reject) => {
    image.onerror = reject;
    image.onload = () => {
      resolve(image);
    };
  });
}

type DrawParamsKey = 'dx' | 'dy' | 'dw' | 'dh'

interface IStroke {
    strokeStyle: string,
    lineWidth: number,
}

/**
 * 将图片原样绘制到Canvas中
 * @param canvas 要绘制的目标Canvas
 * @param src 图片的src
 * @param params drawImage的四个方位参数
 */
// eslint-disable-next-line max-params
export async function drawImageToCanvas(
  canvas: HTMLCanvasElement,
  src: string,
  params: { [ K in DrawParamsKey ]: number },
  stroke?: IStroke,
): Promise<HTMLCanvasElement> {
  const ctx = canvas.getContext('2d');
  if (!ctx) { throw Error('CanvasRenderingContext2D was undefined'); }
  const img = await loadImage(src);
  const {
    dx, dy, dw, dh,
  } = params;
  ctx.drawImage(img, dx, dy, dw, dh);
  if (stroke) { strokeCanvas(ctx, stroke.strokeStyle, stroke.lineWidth); }
  return canvas;
}

/**
 * 将图片按照圆形绘制到Canvas中
 * @param canvas 要绘制的目标Canvas
 * @param src 图片的src
 * @param params drawImage的四个方位参数
 * @param stroke 描边效果的参数
 */
// eslint-disable-next-line max-params
export async function drawImageRoundToCanvas(
  canvas: HTMLCanvasElement,
  src: string,
  params: { [ K in DrawParamsKey ]?: number },
  stroke?: IStroke,
): Promise<HTMLCanvasElement> {
  const ctx = canvas.getContext('2d');
  if (!ctx) { throw Error('CanvasRenderingContext2D was undefined'); }
  const img = await loadImage(src);
  const { dx = 0, dy = 0 } = params;
  ctx.save(); // 保存之前的
  ctx.beginPath();
  const { width, height } = img;
  const r = Math.min(width, height) / 2;
  const d = 2 * r; // 直径
  const cx = dx + r; // 圆弧坐标x
  const cy = dy + r; // 圆弧坐标 y
  ctx.arc(cx, cy, r, 0, 2 * Math.PI);
  if (stroke) { strokeCanvas(ctx, stroke.strokeStyle, stroke.lineWidth); }
  ctx.clip(); // 裁剪
  ctx.drawImage(img, dx, dy, d, d);
  ctx.restore(); // 返回上一状态
  return canvas;
}

/**
 * 将图片按照圆角矩形绘制到Canvas中
 * @param canvas 要绘制的目标Canvas
 * @param src 图片的src
 * @param params drawImage的四个方位参数
 * @param radius 圆角大小
 * @param stroke 描边效果的参数
 */
// eslint-disable-next-line max-params
export async function drawImageRoundRectToCanvas(
  canvas: HTMLCanvasElement,
  src: string,
  params: { [ K in DrawParamsKey ]?: number;},
  radius = 40,
  stroke?: IStroke,
): Promise<HTMLCanvasElement> {
  const ctx = canvas.getContext('2d');
  if (!ctx) { throw Error('CanvasRenderingContext2D was undefined'); }
  const img = await loadImage(src);
  const { dx = 0, dy = 0 } = params;
  ctx.save(); // 保存之前的
  ctx.beginPath();
  const { width, height } = img;
  if (2 * radius > width || 2 * radius > height) { throw Error('radius was too max'); }
  ctx.translate(dx, dy);
  ctx.beginPath();
  drawRoundRectPath(canvas, width, height, radius);
  if (stroke) { strokeCanvas(ctx, stroke.strokeStyle, stroke.lineWidth); }
  ctx.clip();
  ctx.drawImage(img, 0, 0, width, height);
  ctx.restore(); // 返回上一状态
  return canvas;
}

/**
 * 对图像进行描边
 * @param ctx canvas 2d对象
 * @param strokeStyle 描变样式
 * @param lineWidth 描变宽度
 */
export function strokeCanvas(
  ctx: CanvasRenderingContext2D,
  strokeStyle?: string,
  lineWidth?: number,
): void {
  if (!ctx) { throw Error('CanvasRenderingContext2D was undefined'); }
  ctx.save();
  ctx.lineWidth = lineWidth || 2; // 若是给定了值就用给定的值否则给予默认值2
  ctx.strokeStyle = strokeStyle || '#fff';
  ctx.stroke();
}

/**
 * 绘制圆角矩形
 * @param canvas 要绘制的目标Canvas
 * @param width 目标宽度
 * @param height 目标高度
 * @param radius 曲率数值
 */
// eslint-disable-next-line max-params
export function drawRoundRectPath(
  canvas: HTMLCanvasElement,
  width: number, height: number, radius: number,
): HTMLCanvasElement {
  const ctx = canvas.getContext('2d');
  if (!ctx) { throw Error('CanvasRenderingContext2D was undefined'); }
  ctx.beginPath();
  ctx.moveTo(0, radius);
  ctx.lineTo(0, height - radius);
  ctx.quadraticCurveTo(0, height, radius, height);
  ctx.lineTo(width - radius, height);
  ctx.quadraticCurveTo(width, height, width, height - radius);
  ctx.lineTo(width, radius);
  ctx.quadraticCurveTo(width, 0, width - radius, 0);
  ctx.lineTo(radius, 0);
  ctx.quadraticCurveTo(0, 0, 0, radius);
  return canvas;
}
